package com.smartpower.wattmeter.manager;

import android.app.Application;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;

import com.android.basiclib.uitls.CommUtils;
import com.android.basiclib.uitls.Log.LogUtil;
import com.android.basiclib.uitls.ToastUtils;
import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleGattCallback;
import com.clj.fastble.callback.BleNotifyCallback;
import com.clj.fastble.callback.BleScanCallback;
import com.clj.fastble.callback.BleWriteCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.scan.BleScanRuleConfig;
import com.clj.fastble.utils.HexUtil;
import com.smartpower.wattmeter.utils.ByteUtil;
import com.smartpower.wattmeter.utils.CRC16Util;

import java.util.List;
import java.util.UUID;

public class FastBleManager implements IFastBleManager<BleDevice> {

    private static final FastBleManager ourInstance = new FastBleManager();
    private MyFastBleCallback<BleDevice> mMyBleCallback;

    public static FastBleManager getInstance() {
        return ourInstance;
    }

    private FastBleManager() {
    }

    @Override
    public void BLEInit(Application context, MyFastBleCallback<BleDevice> myBleCallback) {

        BleManager.getInstance().init(context);
        BleManager.getInstance()
                .enableLog(true)
                .setReConnectCount(1, 10000)
                .setOperateTimeout(5000);

        //配置扫描规则
        BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
//                .setServiceUuids(serviceUuids)      // 只扫描指定的服务的设备，可选
//                .setDeviceName(true, "Samsung")         // 只扫描指定Name的设备，可选
//                .setDeviceMac(mac)                  // 只扫描指定mac的设备，可选
                .setAutoConnect(false)      // 连接时的autoConnect参数，可选，默认false
                .setScanTimeOut(10000)              // 扫描超时时间，可选，默认10秒
                .build();

        BleManager.getInstance().initScanRule(scanRuleConfig);
        LogUtil.w("BLE框架初始化成功");

        mMyBleCallback = myBleCallback;
    }

    @Override
    public void BLEStarScan() {

        BleManager.getInstance().scan(new BleScanCallback() {
            @Override
            public void onScanStarted(boolean success) {
            }

            @Override
            public void onLeScan(BleDevice bleDevice) {
            }

            @Override
            public void onScanning(BleDevice bleDevice) {
                //不为空的时候才添加
//                if (!CheckUtil.isEmpty(bleDevice.getName())) {
                mMyBleCallback.BLEScanDevices(bleDevice);
//                }
            }

            @Override
            public void onScanFinished(List<BleDevice> scanResultList) {
                //扫描完成
                mMyBleCallback.BLEScanFinish();
            }
        });
    }

    @Override
    public void BLEStopScan() {
        BleManager.getInstance().cancelScan();
    }

    @Override
    public void BLEReadConnect(BleDevice bleDevice) {
        BleManager.getInstance().connect(bleDevice, new BleGattCallback() {
            @Override
            public void onStartConnect() {
            }

            @Override
            public void onConnectFail(BleDevice bleDevice, BleException exception) {
                //连接失败
                mMyBleCallback.BLEConnectFailed();
                ToastUtils.makeText(CommUtils.getContext(), exception.getDescription());
                LogUtil.e("连接失败:" + exception.getDescription());
            }

            @Override
            public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
                //连接成功
                mMyBleCallback.BLEReadConnectSuccess(bleDevice, gatt);
            }

            @Override
            public void onDisConnected(boolean isActiveDisConnected, BleDevice device, BluetoothGatt gatt, int status) {
                //取消连接,如果不是自己手动取消连接的，重新连接
                LogUtil.e("断开连接了");
                if (!isActiveDisConnected) {
                    //重连
                    LogUtil.e("断开连接了再重连");
                    BLEReadConnect(bleDevice);
                }
            }
        });
    }

    String serviceUuidStr = "";
    String characteristicUUidStr = "";

    /**
     * 读取Notify中的数据
     */
    @Override
    public void BLEReadNotifyData(BleDevice bleDevice, BluetoothGatt gatt) {

        if (BleManager.getInstance().isConnected(bleDevice)) {

            //循环遍历全部的服务和通道，找到指定的通道
            List<BluetoothGattService> serviceList = gatt.getServices();

            for (BluetoothGattService service : serviceList) {
                UUID serviceUuid = service.getUuid();
                serviceUuidStr = serviceUuid.toString();

                List<BluetoothGattCharacteristic> characteristicList = service.getCharacteristics();
                for (BluetoothGattCharacteristic characteristic : characteristicList) {
                    UUID uuid_chara = characteristic.getUuid();
                    characteristicUUidStr = uuid_chara.toString();
                    if (characteristicUUidStr.equals("49535343-aca3-481c-91ec-d85e28a60318")) {
                        break;
                    }
                }
            }

            LogUtil.w("serviceUuid:" + serviceUuidStr);
            LogUtil.w("chara_uuid:" + characteristicUUidStr);

            //连接成功开始接受通知数据
            BleManager.getInstance().notify(bleDevice, serviceUuidStr, characteristicUUidStr,
                    new BleNotifyCallback() {
                        @Override
                        public void onNotifySuccess() {
                            // 打开通知操作成功
                            LogUtil.w("打开通知成功,等待接收数据");
                        }

                        @Override
                        public void onNotifyFailure(BleException exception) {
                            // 打开通知操作失败
                            LogUtil.w("打开通知操作失败" + exception.getDescription());
                            ToastUtils.makeText(CommUtils.getContext(), "打开通知操作失败." + exception.getDescription());
                        }

                        @Override
                        public void onCharacteristicChanged(byte[] data) {
                            // 打开通知后，设备发过来的数据将在这里出现
                            handleReadNotifyData(data, bleDevice);
                        }
                    });


            //发送开始接受的指令
            sendReadNotifyDirect(bleDevice);
        } else {
            ToastUtils.makeText(CommUtils.getContext(), "连接失效了,需要重新连接");
        }

    }

    //延时发送读取通知的指令
    private void sendReadNotifyDirect(BleDevice bleDevice) {
        contentLenght = 1;
        allContent = new StringBuilder();

        //连接成功直接读取数据
        if (BleManager.getInstance().isConnected(bleDevice)) {
            CommUtils.getHandler().postDelayed(() -> {
                //写入数据命令-接受Notify的数据
                byte[] bytes = HexUtil.hexStringToBytes("A55A000001BB0196");
                LogUtil.w("准备写入数据命令:" + bytes.toString());
                BleManager.getInstance().write(bleDevice, serviceUuidStr, characteristicUUidStr, bytes
                        , new BleWriteCallback() {
                            @Override
                            public void onWriteSuccess(int current, int total, byte[] justWrite) {
                            }

                            @Override
                            public void onWriteFailure(BleException exception) {
                                LogUtil.w("写入数据命令失败:" + exception.getDescription());
                            }
                        });
            }, 300);
        } else {
            ToastUtils.makeText(CommUtils.getContext(), "连接失效了,需要重新连接");
        }

    }

    StringBuilder allContent = new StringBuilder();
    int contentLenght = 1;

    /**
     * 处理通知的接受数据（转码解析数据）
     */
    private void handleReadNotifyData(byte[] data, BleDevice bleDevice) {

        allContent.append(ByteUtil.bytesToHex(data));

//        buffer.writeBytes(data);

        if (data.length > 5 && ByteUtil.bytesToHex(ByteUtil.subBytes(data, 0, 2)).toUpperCase().equals("A55A")) {
            LogUtil.w("确定了帧头");
            //拿到长度信息
            String length1 = ByteUtil.bytesToHex(ByteUtil.subBytes(data, 2, 1));
            String length2 = ByteUtil.bytesToHex(ByteUtil.subBytes(data, 3, 1));
            //长度16进制转10进制
            contentLenght = Integer.parseInt(length2 + length1, 16);

            LogUtil.e("contentLenght:" + contentLenght);
        }

        if (ByteUtil.bytesToHex(ByteUtil.subBytes(data, data.length - 1, 1)).equals("96")) {
            LogUtil.w("确定了帧尾");

            //校验长度是否一致
            byte[] allBytes = ByteUtil.hexToBytes(allContent.toString());
            int allLength = allBytes.length;
            int subLength = allLength - 8;
            LogUtil.e("allLength:" + allLength);
            if (contentLenght == subLength) {
                //验证成功
                byte[] jsonBytes = ByteUtil.subBytes(allBytes, 5, subLength);
                String jsonStr = new String(jsonBytes);
                LogUtil.w("jsonStr:" + jsonStr);

                //关闭通知
                BleManager.getInstance().stopNotify(bleDevice, serviceUuidStr, characteristicUUidStr);

                //回调
                mMyBleCallback.BLENotifyDataSuccessCallback(jsonStr, bleDevice);

            } else {
                //验证失败,从新读取
                ToastUtils.makeText(CommUtils.getContext(), "验证失败,重新读取");
                contentLenght = 1;
                allContent = new StringBuilder();
                //发送开始接受的指令
                sendReadNotifyDirect(bleDevice);
            }
        }

    }

    //写入数据到设备
    @Override
    public void BLEWriteData(BleDevice bleDevice, String jsonStr) {
        if (BleManager.getInstance().isConnected(bleDevice)) {
            //延时打开通知开关
            CommUtils.getHandler().postDelayed(() -> {
                //连接成功开始接受通知数据
                BleManager.getInstance().notify(bleDevice, serviceUuidStr, characteristicUUidStr,
                        new BleNotifyCallback() {
                            @Override
                            public void onNotifySuccess() {
                                // 打开通知操作成功
                                LogUtil.w("打开写入通知成功,等待接收数据");
                            }

                            @Override
                            public void onNotifyFailure(BleException exception) {
                                // 打开通知操作失败
                                LogUtil.w("打开写入通知成功" + exception.getDescription());
                                ToastUtils.makeText(CommUtils.getContext(), "打开通知操作失败." + exception.getDescription());
                            }

                            @Override
                            public void onCharacteristicChanged(byte[] data) {
                                // 打开通知后，设备发过来的数据将在这里出现
                                handleWriteNotifyData(data, bleDevice);
                            }
                        });

                //开始写数据
                byte[] jsonStrByte = jsonStr.getBytes();
                String hexJsonContentStr = ByteUtil.bytesToHex(jsonStrByte);
                String hexLengthStr = Integer.toHexString(jsonStrByte.length);

                //赋值长度
                if (hexLengthStr.length() == 2) {
                    hexLengthStr = hexLengthStr + "00";
                } else if (hexLengthStr.length() == 3) {
                    hexLengthStr = "0" + hexLengthStr;
                    String substring1 = hexLengthStr.substring(0, 2);
                    String substring2 = hexLengthStr.substring(2);
                    hexLengthStr = substring2 + substring1;
                } else if (hexLengthStr.length() == 4) {
                    String substring1 = hexLengthStr.substring(0, 2);
                    String substring2 = hexLengthStr.substring(2);
                    hexLengthStr = substring2 + substring1;
                }

//            String crc1 = CRC16Util.getCRC(ByteUtil.hexToBytes("A55A" + hexLengthStr + "03" + hexJsonContentStr));
//            String crc2 = CRC16Util.getCRC2(ByteUtil.hexToBytes("A55A" + hexLengthStr + "03" + hexJsonContentStr));
                String crc3 = CRC16Util.getCRC3(ByteUtil.hexToBytes("A55A" + hexLengthStr + "03" + hexJsonContentStr));

                String hexWriteStr = "A55A" + hexLengthStr + "03" + hexJsonContentStr + crc3 + "96";

                LogUtil.w("hexLengthStr:" + hexLengthStr);
                LogUtil.w("hexJsonContentStr:" + hexJsonContentStr);
                LogUtil.w("crc3:" + crc3);
                LogUtil.w("hexWriteStr:" + hexWriteStr);

                //延时发送写入数据指令
                sendWriteNotifyDirect(bleDevice, hexWriteStr);

            }, 250);


        } else {
            ToastUtils.makeText(CommUtils.getContext(), "连接失效了,需要重新连接");
        }

    }

    //发送开始接受的指令
    private void sendWriteNotifyDirect(BleDevice bleDevice, String hexStr) {
        contentLenght = 1;
        allContent = new StringBuilder();

        //连接成功直接读取数据
        if (BleManager.getInstance().isConnected(bleDevice)) {
            CommUtils.getHandler().postDelayed(() -> {
                //写入数据命令-接受Notify的数据
                BleManager.getInstance().write(
                        bleDevice,
                        serviceUuidStr,
                        characteristicUUidStr,
                        HexUtil.hexStringToBytes(hexStr),
                        new BleWriteCallback() {

                            @Override
                            public void onWriteSuccess(final int current, final int total, final byte[] justWrite) {
                                LogUtil.w("写入成功");
                            }

                            @Override
                            public void onWriteFailure(final BleException exception) {
                                LogUtil.w("写入数据命令失败:" + exception.getDescription());
                            }
                        });
            }, 300);
        } else {
            ToastUtils.makeText(CommUtils.getContext(), "连接失效了,需要重新连接");
        }

    }


    /**
     * 处理通知的接受数据（转码解析数据）
     */
    private void handleWriteNotifyData(byte[] data, BleDevice bleDevice) {

        allContent.append(ByteUtil.bytesToHex(data));

//        buffer.writeBytes(data);

        if (data.length > 5 && ByteUtil.bytesToHex(ByteUtil.subBytes(data, 0, 2)).toUpperCase().equals("A55A")) {
            LogUtil.w("确定了帧头");
            //拿到长度信息
            String length1 = ByteUtil.bytesToHex(ByteUtil.subBytes(data, 2, 1));
            String length2 = ByteUtil.bytesToHex(ByteUtil.subBytes(data, 3, 1));
            //长度16进制转10进制
            contentLenght = Integer.parseInt(length2 + length1, 16);

            LogUtil.e("contentLenght:" + contentLenght);
        }

        if (ByteUtil.bytesToHex(ByteUtil.subBytes(data, data.length - 1, 1)).equals("96")) {
            LogUtil.w("确定了帧尾");

            //校验长度是否一致
            byte[] allBytes = ByteUtil.hexToBytes(allContent.toString());
            int allLength = allBytes.length;
            int subLength = allLength - 8;
            LogUtil.e("allLength:" + allLength);
            if (contentLenght == subLength) {
                //验证成功
                byte[] jsonBytes = ByteUtil.subBytes(allBytes, 5, subLength);
                String jsonStr = new String(jsonBytes);
                LogUtil.w("jsonStr:" + jsonStr);

                //关闭通知
                BleManager.getInstance().stopNotify(bleDevice, serviceUuidStr, characteristicUUidStr);

                //回调
                mMyBleCallback.BLENotifyDataSuccessCallback(jsonStr, bleDevice);

            } else {
                //验证失败,从新读取
                ToastUtils.makeText(CommUtils.getContext(), "验证失败,重新写入");
                contentLenght = 1;
                allContent = new StringBuilder();

            }
        }
    }

    @Override
    public void destroyBLE() {
        if (BleManager.getInstance() != null) {
            BleManager.getInstance().disconnectAllDevice();
            BleManager.getInstance().destroy();
        }

    }
}
